package HLA1.federate;



import java.io.BufferedReader;
import java.io.File;
import java.io.InputStreamReader;
import java.net.MalformedURLException;

import hla.rti.AttributeHandleSet;
import hla.rti.ConcurrentAccessAttempted;
import hla.rti.FederateNotExecutionMember;
import hla.rti.FederateOwnsAttributes;
import hla.rti.FederatesCurrentlyJoined;
import hla.rti.FederationExecutionAlreadyExists;
import hla.rti.FederationExecutionDoesNotExist;
import hla.rti.InvalidResignAction;
import hla.rti.LogicalTime;
import hla.rti.LogicalTimeInterval;
import hla.rti.RTIambassador;
import hla.rti.RTIexception;
import hla.rti.RTIinternalError;
import hla.rti.ResignAction;
import hla.rti.SuppliedAttributes;
import hla.rti.SuppliedParameters;
import hla.rti.jlc.EncodingHelpers;
import hla.rti.jlc.RtiFactoryFactory;

import org.portico.impl.hla13.types.DoubleTime;
import org.portico.impl.hla13.types.DoubleTimeInterval;

import HLA1.control.*;
import HLA1.generatori.*;
import HLA2.federate.Federate2Ambassador;

public class Federate1
{
	
	public int objectHandle;
	public static final String READY_TO_RUN = "ReadyToRun";
	public RTIambassador rtiamb;
	public Federate1Ambassador fedamb;



	public void log( String message )
	{
		System.out.println( "firstFederate  : " + message );
	}

	public void waitForUser()
	{
		log( " >>>>>>>>>> Press Enter to Continue <<<<<<<<<<" );
		BufferedReader reader = new BufferedReader( new InputStreamReader(System.in) );
		try
		{
			reader.readLine();
		}
		catch( Exception e )
		{
			log( "Error while waiting for user input: " + e.getMessage() );
			e.printStackTrace();
		}
	}


	public LogicalTime convertTime( double time )
	{
		// PORTICO SPECIFIC!!
		return new DoubleTime( time );
	}

	public LogicalTimeInterval convertInterval( double time )
	{
		// PORTICO SPECIFIC!!
		return new DoubleTimeInterval( time );
	}

	public void runFederate( String federateName ) throws RTIexception
	{
		// 1. creazione dell'RTI Ambassador
		rtiamb = RtiFactoryFactory.getRtiFactory().createRtiAmbassador();


		// 2. creazione della federazione

		try
		{
			File fom = new File( "mis.fed" );
			rtiamb.createFederationExecution( "Federazione",
					fom.toURI().toURL() );
			log( "Created Federation" );
		}
		catch( FederationExecutionAlreadyExists exists )
		{
			log( "Didn't create federation, it already existed" );
		}
		catch( MalformedURLException urle )
		{
			log( "Exception processing fom: " + urle.getMessage() );
			urle.printStackTrace();
			return;
		}

		// 3. creazione del federateAmbassador e partecipazione alla federazione
		fedamb = new Federate1Ambassador();
		rtiamb.joinFederationExecution( federateName, "Federazione", fedamb );
		log( "Joined Federation as " + federateName );

		// 4. annuncia il punto di sincronizzazione

		rtiamb.registerFederationSynchronizationPoint( READY_TO_RUN, null );
		// attendi che il punto di sincronizzazione sia annunciato
		while( fedamb.isAnnounced == false )
		{
			rtiamb.tick();
		}

		waitForUser();
		// 5. raggiungi il punto di sincronizzazione e attendi gli altri
		rtiamb.synchronizationPointAchieved( READY_TO_RUN );
		log( "Achieved sync point: " +READY_TO_RUN+ ", waiting for federation..." );
		while( fedamb.isReadyToRun == false )
		{
			rtiamb.tick();
		}

		// 6. abilita gestione del tempo
		
		enableTimePolicy();
		log( "Time Policy Enabled" );

		// 7. pubblica e sottoscrivi
		
		publishAndSubscribe();
		log( "Published and Subscribed" );

		// 8. registra un oggetto da aggiornare

		objectHandle = registerObject();
	}

	public void enableTimePolicy() throws RTIexception
	{

		LogicalTime currentTime = convertTime( fedamb.federateTime );
		LogicalTimeInterval lookahead = convertInterval( fedamb.federateLookahead );

		////////////////////////////
		// enable time regulation //
		////////////////////////////
		this.rtiamb.enableTimeRegulation( currentTime, lookahead );

		// tick until we get the callback
		while( fedamb.isRegulating == false )
		{
			rtiamb.tick();
		}

		/////////////////////////////
		// enable time constrained //
		/////////////////////////////
		this.rtiamb.enableTimeConstrained();

		// tick until we get the callback
		while( fedamb.isConstrained == false )
		{
			rtiamb.tick();
		}
	}

	public void publishAndSubscribe() throws RTIexception
	{
		int classHandle = rtiamb.getObjectClassHandle( "ObjectRoot.Disk" );
		int aaHandle    = rtiamb.getAttributeHandle( "random", classHandle );
		int abHandle    = rtiamb.getAttributeHandle( "tempo", classHandle );
		int acHandle    = rtiamb.getAttributeHandle( "coda", classHandle );
		int adHandle    = rtiamb.getAttributeHandle( "job", classHandle );

		AttributeHandleSet attributes =
			RtiFactoryFactory.getRtiFactory().createAttributeHandleSet();
		attributes.add( aaHandle );
		attributes.add( abHandle );
		attributes.add( acHandle );
		attributes.add( adHandle );

		rtiamb.publishObjectClass( classHandle, attributes );

		rtiamb.subscribeObjectClassAttributes( classHandle, attributes );

		//sottoscrizione interazione
		int interactionHandle = rtiamb.getInteractionClassHandle( "InteractionRoot.Comando" );
		rtiamb.publishInteractionClass( interactionHandle );
		rtiamb.subscribeInteractionClass( interactionHandle );
	}


	public int registerObject() throws RTIexception
	{
		int classHandle = rtiamb.getObjectClassHandle( "ObjectRoot.Disk" );
		return rtiamb.registerObjectInstance( classHandle );
	}


	public void updateAttributeValues( int objectHandle , String s, double l) throws RTIexception
	{
		SuppliedAttributes attributes =
			RtiFactoryFactory.getRtiFactory().createSuppliedAttributes();

		// generate the new values
		// we use EncodingHelpers to make things nice friendly for both Java and C++
		byte[] randomValue = EncodingHelpers.encodeDouble(l);

		int classHandle = rtiamb.getObjectClass( objectHandle );
		int randomHandle = rtiamb.getAttributeHandle( "random", classHandle );

		// put the values into the collection
		attributes.add( randomHandle, randomValue );

		byte[] tag = EncodingHelpers.encodeString (s);

		LogicalTime time = convertTime( fedamb.federateTime +
				fedamb.federateLookahead );
		rtiamb.updateAttributeValues( objectHandle, attributes, tag, time );
	}

	public void mandaJob( int objectHandle , String s, int i) throws RTIexception
	{
		SuppliedAttributes attributes =
			RtiFactoryFactory.getRtiFactory().createSuppliedAttributes();

		// generate the new values
		// we use EncodingHelpers to make things nice friendly for both Java and C++
		byte[] randomValue = EncodingHelpers.encodeInt(i);

		int classHandle = rtiamb.getObjectClass( objectHandle );
		int randomHandle = rtiamb.getAttributeHandle( "job", classHandle );

		// put the values into the collection
		attributes.add( randomHandle, randomValue );

		byte[] tag = EncodingHelpers.encodeString (s);

		LogicalTime time = convertTime( fedamb.federateTime +
				fedamb.federateLookahead );
		rtiamb.updateAttributeValues( objectHandle, attributes, tag, time );
	}

	public void sendInteraction(String s) throws RTIexception
	{
		SuppliedParameters parameters =
			RtiFactoryFactory.getRtiFactory().createSuppliedParameters();

		byte[] xaValue = EncodingHelpers.encodeString( "xa:" + fedamb.federateTime );

		// get the handles
		int classHandle = rtiamb.getInteractionClassHandle( "InteractionRoot.Comando" );
		int xaHandle = rtiamb.getParameterHandle( "msg", classHandle );

		// put the values into the collection
		parameters.add( xaHandle, xaValue );

		//////////////////////////
		// send the interaction //
		//////////////////////////
		byte[] tag = EncodingHelpers.encodeString( s );
		//rtiamb.sendInteraction( classHandle, parameters, tag );

		LogicalTime time = convertTime( fedamb.federateTime +
				fedamb.federateLookahead );
		rtiamb.sendInteraction( classHandle, parameters, tag, time );
	}

	public void advanceTime( double timestep ) throws RTIexception
	{
		// request the advance
		fedamb.isAdvancing = true;
		LogicalTime newTime = convertTime( fedamb.federateTime + timestep );
		rtiamb.timeAdvanceRequest( newTime );

		// wait for the time advance to be granted. ticking will tell the
		// LRC to start delivering callbacks to the federate
		while( fedamb.isAdvancing )
		{
			rtiamb.tick();
		}
	}

	public void deleteObject( int handle ) throws RTIexception
	{
		rtiamb.deleteObjectInstance( handle, null ); // no tag, we're lazy
	}

	public void lasciaFederazione()throws RTIexception{
		// 10. cancella gli oggetti creati
		deleteObject( objectHandle );
		log( "Deleted Object, handle=" + objectHandle );

		// 11. abbandona la federazione 

		rtiamb.resignFederationExecution( ResignAction.NO_ACTION );
		log( "Resigned from Federation" );

		// 12. prova a distruggere la federazione

		try
		{
			rtiamb.destroyFederationExecution( "Federazione" );
			log( "Destroyed Federation" );
		}
		catch( FederationExecutionDoesNotExist dne )
		{
			log( "No need to destroy federation, it doesn't exist" );
		}
		catch( FederatesCurrentlyJoined fcj )
		{
			log( "Didn't destroy federation, federates still joined" );
		}
	}

    public void mandaStato(int objectHandle, boolean b, String s) throws RTIexception, RTIinternalError
	{
		SuppliedAttributes attributes = RtiFactoryFactory.getRtiFactory().createSuppliedAttributes();

		byte[] stato  = EncodingHelpers.encodeBoolean(b);

		int classHandle = rtiamb.getObjectClass(objectHandle);
		int statoHandle = rtiamb.getAttributeHandle("stato", classHandle);
		attributes.add(statoHandle, stato);

		byte[] tag = EncodingHelpers.encodeString(s);
		LogicalTime time = convertTime( fedamb.federateTime + fedamb.federateLookahead );
		rtiamb.updateAttributeValues(objectHandle, attributes, tag, time);
	}

	//                     STATIC METHODS
	//----------------------------------------------------------
	public static void main( String[] args ) throws RTIexception
	{
        
		Federate1 simulazione = new Federate1();
		// get a federate name
		String federateName = "firstFederate";
		if( args.length != 0 )
		{
			federateName = args[0];
		}

		simulazione.runFederate( federateName );
		Simulatore s = new Simulatore(10, simulazione);
		double seme = s.getG().generaSeme();
		System.out.println("Seme1= "+seme);
		simulazione.updateAttributeValues(simulazione.objectHandle, "semeD1", seme);
		simulazione.advanceTime(2.0);
//		*********************************************************************
//		System.out.println("TEMPO FEDERAZIONE "+s.simulazione.fedamb.federateTime);
//		*********************************************************************
		seme = s.getG().generaSeme();
		System.out.println("Seme2= "+seme);
		simulazione.updateAttributeValues(simulazione.objectHandle, "semeD2", seme);
		simulazione.advanceTime(2.0);
//		*********************************************************************
//		System.out.println("TEMPO FEDERAZIONE "+s.simulazione.fedamb.federateTime);
//		*********************************************************************
		Main x = new Main();
        x.startSim(s);
        
         simulazione.lasciaFederazione();
	}
}
